import os
import math
import random
import datetime
import calendar
import sys
import Cookie
from urllib import urlencode
from urlparse import urlunparse
from xml.dom import minidom

#from google.appengine.ext import db
#from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp.util import run_wsgi_app
from restclient import RESTClient

from crypto import encodeData, decodeData 

#import pdb; pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__).set_trace()

_ = lambda x: os.path.join(os.path.dirname(__file__), x) 

DOMAIN = 'updatelog.com'

class TimeEntry(object):
    def __init__(self, hours, description, projectref):
        self.hours = hours
        self.description = description
        self.projectref = projectref

class Day(object):
    styles = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']

    def __init__(self, date, time_entries=[]):
        self.day = date
        self.time_entries = time_entries
        # 0 date is a special case
        if not (date >= 0 and date <= 31):
            raise ValueError('%d number is not a day number' % date)

    def __nonzero__(self):
        return self.day

    @property
    def time(self):
        return sum([i.hours for i in self.time_entries])

    def formatted_time(self):
        return '%.2f' % self.time

    @property
    def style(self):
        style = ['day']
        if self.day == 0:
            return 'day prev'
        time = int(math.ceil(self.time))
        if time and time <= 8:
            return 'day ' + self.styles[time-1]
        elif time:
            return 'day more'
        else:
            return 'day'

class calRequestHandler(webapp.RequestHandler):
    def __init__(self, *args, **kwargs):
        super(calRequestHandler, self).__init__(*args, **kwargs)
     
    # absence of this method kills.
    def renderToResponse(self, page_path, values):
        self.response.out.write(template.render(page_path, values))


    def absoluteUrl(self, subdomain, relative_url='', params='', query='', fragment=''):
        if type(query) == dict:
            query = urlencode(query)
        return urlunparse(('https', '%s.%s' % (subdomain, DOMAIN), relative_url, params, query, fragment))


class LoginPage(calRequestHandler):
    
    def __init__(self, *args, **kwargs):
        super(LoginPage, self).__init__(*args, **kwargs)
        client = RESTClient()
        client.requestHeaders.update({
            'Content-Type': 'application/xml',
            'Accept': 'application/xml'
        })
        self._client = client


    def getSubjectId(self, login, pwd, subdomain):
        """ 
        Get 'subject_id' for report query - it is id of logged in user.
        """
        self._client.setCredentials(
            login, 
            pwd, 
        )
        self._client.get(self.absoluteUrl(subdomain, '/me.xml'))
        if self._client.status == 200:
            dom = minidom.parseString(self._client.contents)
            return str(dom.getElementsByTagName('id')[0].firstChild.nodeValue)
        else:
            raise Exception('Can\'t get subject_id')


    def get(self):
        subdomain = self.request.cookies.get('subdomain', '')
        values = { 'subdomain': subdomain } 
        self.renderToResponse(_('login.html'), values)

    def post(self):
        if not (self.request.get('login') and self.request.get('password')): 
           self.redirect('/login')

        #import pdb; pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__).set_trace()
        login = self.request.get('login')
        pwd = self.request.get('password')
        subdomain = self.request.get('subdomain', self.request.cookies.get('subdomain'))
        data = [login, pwd, ]

        try:
            subjectId = self.getSubjectId(login, pwd, subdomain)
        except:
            self.renderToResponse(_('login.html'), { 'error_msg': True })
            return

        data.append(subjectId) 
        save_subd = self.request.get('save_subdomain')
        
        if save_subd: 
            subd_cookie = 'subdomain=%s; expires=Fri, 31-Dec-2010 23:59:59 GMT;' % subdomain 
        else:
            # in case we don't want to save the subdomain "permanently", 
            # we include it into the SSID, thus the subdomain is clear in the next session
            data.append(subdomain)
            subd_cookie = 'subdomain=%s; expires=Fri, 31-Dec-2008 23:59:59 GMT;' % subdomain 

        self.response.headers.add_header('Set-Cookie', subd_cookie)

        ssid_cookie = 'ssid=%s' % encodeData(tuple(data)) 
        self.response.headers.add_header('Set-Cookie', ssid_cookie)

        self.redirect('/')

class LogoutPage(calRequestHandler):
    
    def __init__(self, *args, **kwargs):
        super(LogoutPage, self).__init__(*args, **kwargs)

    def get(self):
        self.post()

    def post(self):
        ssid = self.request.cookies.get('ssid', '') 
        data = decodeData(ssid)
        if not data:
            self.redirect('/login')
            return
        self.response.headers['Set-Cookie'] = 'ssid=%s; expires=Fri, 31-Dec-2008 23:59:59 GMT;' % ssid
        self.renderToResponse(_('logout.html'), {})
        

class MainPage(calRequestHandler):

    def __init__(self, *args, **kwargs):
        super(MainPage, self).__init__(*args, **kwargs)
        client = RESTClient()
        client.requestHeaders.update({
            'Content-Type': 'application/xml',
            'Accept': 'application/xml'
        })
        self._client = client

    def get(self):
        self.post()

    def post(self):
        #import pdb; pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__).set_trace()
        isSessioned = False
        subdomain = None

        if 'ssid' in self.request.cookies:
            data = decodeData(self.request.cookies['ssid'])
            if data:
                isSessioned = True
                login, pwd, subjectId = data[:3]
                if len(data) > 3:
                    subdomain = data[3]

        if not isSessioned:
            self.redirect('/login')
            return

        self._client.setCredentials(
            login, 
            pwd, 
        )
        if subdomain is None:
            subdomain = self.request.cookies.get('subdomain','')
 
        project = self.request.get('project_filter', 'all')
        dt = datetime.datetime.now()
        year = self.request.get('year_filter', dt.year)
        month = self.request.get('month_filter', dt.month)
        try:
            year = int(year)
            if year < 2000 or year > 3000:
                raise Exception()
        except: pass
        try:
            month = int(month)
            if month < 1 or month > 12:
                raise Exception()
        except: pass
        dt = dt.replace(month=month, year=year) 
        report_raw = self.getMonthTimeReport(subdomain, dt.year, dt.month, subjectId, project)
        month_info = []
        report = {'entries': []}  
        total_hours = 0
        for week_raw in calendar.monthcalendar(dt.year, dt.month):
            week = []
            month_info.append(week)
            for day in week_raw:
                if day in report_raw[0]:
                    day_obj = Day(day, report_raw[0][day])
                    total_hours += day_obj.time
                    report['entries'].append(day_obj)
                else:
                    day_obj = Day(day)
                week.append(day_obj)

        report_raw[1]['all'] = '-- All projects --'
        # sort projects by a name
        sort_cmp = lambda x, y: cmp(x[1],y[1])
        sorted_projects = sorted(report_raw[1].items(), sort_cmp)
        now = datetime.datetime.now()
        report.update({
            'total_hours': total_hours,
            'weeks': month_info,
            'project_filter': project,
            'year_filter': dt.year, 
            'month_filter': dt.month,
            'years': [i + 1 for i in range(now.year - 5, now.year)],
            'projects': sorted_projects,  
            'username': login,
            'id': subjectId,
            'root': self.absoluteUrl(subdomain), 
            'dt': now,
        })

        values = {
            'report': report,
        }
        self.renderToResponse(_('index.html'), values)


    def getAllProjects(self, subdomain):
        """
        Fetch projects info to resolve ids to names: one general request
        instead of fetching one by one separate projects
        """
        projects_dict = {}
        url = self.absoluteUrl(subdomain, '/projects.xml')
        self._client.get(url)

        project_dom = minidom.parseString(self._client.contents)
        for project in project_dom.getElementsByTagName('projects')[0]\
            .getElementsByTagName('project'):

            id = project.getElementsByTagName('id')[0].firstChild.nodeValue
            name = project.getElementsByTagName('name')[0].firstChild.nodeValue
            projects_dict[id] = name
        return projects_dict
               

    def getMonthTimeReport(self, subdomain, year, month, subject_id, project_filter):
        
        from_ = '%d%02d01' % (year, month)
        to = '%d%02d%02d' % (year, month, max(calendar.monthcalendar(year, month)[-1]))
        report = {}
        projects_dict = None
        url = self.absoluteUrl(subdomain, '/time_entries/report.xml', query={
            'from': from_, 
            'to': to,
            'subject_id': subject_id
        })
        #try:
        #    self._client.get(url)
        #except:
        #import pdb; pdb.Pdb(stdin=sys.__stdin__, stdout=sys.__stdout__).set_trace()
        self._client.get(url);
        if self._client.status == 200:
            dom = minidom.parseString(self._client.contents)
            projects_dict = self.getAllProjects(subdomain) 
    
            for time_entry in dom.getElementsByTagName('time-entry'):
                proj_id = time_entry.getElementsByTagName('project-id')[0].firstChild.nodeValue
                if project_filter != 'all' and proj_id != project_filter:
                    continue 
                day = time_entry.getElementsByTagName('date')[0].firstChild.nodeValue
                day = int(day.split('-')[-1])
                hours = time_entry.getElementsByTagName('hours')[0].firstChild.nodeValue
                hours = float(hours)
                proj_name = projects_dict[proj_id]
                try:
                    description = time_entry.getElementsByTagName('description')[0].firstChild.nodeValue
                except:
                    description = ''
                entries = report.setdefault(day, [])
                entries.append(TimeEntry(hours, description, (proj_id, proj_name)))
        else:
            raise Exception('Bad HTTP status')
        return (report, projects_dict)

application = webapp.WSGIApplication([
    ('/', MainPage),
    ('/login', LoginPage),
    ('/logout', LogoutPage),
], debug=True)

def main():
    run_wsgi_app(application)
    
if __name__ == "__main__":
    main()
